package modbus

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"modbus-dtu-plugin/mqtt"
	parsingmodbus "modbus-dtu-plugin/parsing_modbus"
	parsingvalues "modbus-dtu-plugin/parsing_values"
	"modbus-dtu-plugin/zaplog"
	"net"
	"strings"
	"time"

	"go.uber.org/zap"
)

// 连接成功后，监听服务端数据
type ModbusClient struct {
	Mode            string //模式：RTU TCP
	Status          bool   //连接状态
	ListenStatus    bool
	ServerAddr      string
	Conn            net.Conn
	Quit            chan struct{} //退出监听
	SubDeviceReqMap map[string]SubDevice
	AccessToken     string //网关设备token
}

func (m *ModbusClient) NewModbusClient(mode string, serverAddr string, subDeviceReqMap map[string]SubDevice, AccessToken string) *ModbusClient {
	m.Mode = mode
	m.ServerAddr = serverAddr
	m.Status = false
	m.ListenStatus = false
	m.Conn = nil
	m.Quit = nil
	m.SubDeviceReqMap = subDeviceReqMap
	m.AccessToken = AccessToken
	return m
}

// 连接失败后，会不断重连
func (m *ModbusClient) Connect() error {
	for {
		zaplog.Logger.Debug("connecting to server", zap.String("address", m.ServerAddr))
		conn, err := net.Dial("tcp", m.ServerAddr)
		if err != nil {
			zaplog.Logger.Warn("connect to server failed" + err.Error())
			time.Sleep(5 * time.Second)
			continue
		}

		m.Conn = conn
		m.Status = true
		mqtt.SendStatus(m.AccessToken, "1")
		zaplog.Logger.Info("Connected to server", zap.String("address", m.ServerAddr))
		m.Listen()
		fmt.Println("连接成功")
		return nil
	}
}

func (m *ModbusClient) Disconnect() error {
	if !m.Status {
		zaplog.Logger.Info("Connection already closed")
		return nil
	}

	m.Status = false
	mqtt.SendStatus(m.AccessToken, "0")
	close(m.Quit)

	err := m.Conn.Close()
	if err != nil {
		zaplog.Logger.Warn("connect to server failed" + err.Error())
		return fmt.Errorf("close connection failed: %w", err)
	}

	m.Conn = nil
	zaplog.Logger.Info("Connection closed")
	return nil
}

func (m *ModbusClient) Listen() {
	go m.listen()
}

func (m *ModbusClient) listen() {
	m.Quit = make(chan struct{})
	m.ListenStatus = true
	defer func() {
		m.ListenStatus = false
		close(m.Quit)
	}()
	for {
		select {
		case <-m.Quit:
			zaplog.Logger.Info("listen goroutine quit")
			return
		default:
			buf := make([]byte, 1024)
			n, err := m.Conn.Read(buf)
			if err != nil {
				zaplog.Logger.Error("read from server failed", zap.Error(err))
				m.reconnect()
				continue
			}
			realBuf := buf[:n]
			zaplog.Logger.Info("received from server", zap.String("data", string(buf[:n])))
			// 解析modbus请求报文
			var modbusMessage parsingmodbus.ModbusMessage
			// 获取m.SubDeviceReqMap中的第一个子设备
			for _, v := range m.SubDeviceReqMap {
				modbusMessage.NewModbusMessage(m.Mode, v.DeviceConfig.ByteOrder)
				break
			}
			err = modbusMessage.SetRequestData(realBuf)
			if err != nil {
				zaplog.Logger.Error("parse modbus request failed", zap.Error(err))
				continue
			}
			// 读取modbus响应的数据
			select {
			case <-m.Quit:
				zaplog.Logger.Info("listen goroutine quit")
				return
			default:
				rsp_buf := make([]byte, 1024)
				n, err = m.Conn.Read(rsp_buf)
				if err != nil {
					zaplog.Logger.Error("read from server failed", zap.Error(err))
					m.reconnect()
					continue
				}
				realBuf := rsp_buf[:n]
				zaplog.Logger.Info("received from server", zap.String("data", string(rsp_buf[:n])))
				// 解析modbus响应报文
				err = modbusMessage.SetResponseData(realBuf)
				if err != nil {
					zaplog.Logger.Error("parse modbus response failed", zap.Error(err))
					continue
				}
				// 发送modbus响应报文到平台
				err = m.SendResponseToPlatform(modbusMessage)
				if err != nil {
					zaplog.Logger.Error("send modbus response to platform failed", zap.Error(err))
					continue
				}
			}
		}
	}
}

func (m *ModbusClient) reconnect() {
	m.Disconnect()
	for {
		err := m.Connect()
		if err == nil {
			return
		}
		zaplog.Logger.Warn("reconnect failed, retrying in 5 seconds", zap.Error(err))
		time.Sleep(5 * time.Second)
	}
}

// 发送modbus响应报文到平台
func (m *ModbusClient) SendResponseToPlatform(modbusMessage parsingmodbus.ModbusMessage) error {
	fmt.Println("SendResponseToPlatform", modbusMessage.RequestData)
	// 获取子设备key
	subDeviceKey := fmt.Sprintf("%d_%d_%d", modbusMessage.Request.SlaveId, modbusMessage.Request.Function, modbusMessage.Request.Address)
	// 获取子设备
	subDevice, ok := m.SubDeviceReqMap[subDeviceKey]
	if !ok {
		return fmt.Errorf("sub device not found")
	}
	// 获取子设备的网关id
	gatewayId := subDevice.DeviceConfig.GatewayId
	fmt.Println("plugin/modbus/", "需要根据网关id获取token:", gatewayId)
	// payload是{sub_device_addr:{key:value...}
	var parseRequestData parsingvalues.ParseRequestData
	keyList := strings.Split(subDevice.DeviceConfig.Key, ",")
	parseRequestData.NewParseRequestData(modbusMessage.Request.Function, modbusMessage.Response.Data, subDevice.DeviceConfig.DataType, keyList, "big")
	payload, err := parseRequestData.ParseRequestData()
	if err != nil {
		return err
	}
	token := subDevice.AccessToken
	fmt.Println("payload:", string(payload))
	// 发送payload到平台
	mqtt.Send(payload, token)
	return nil
}

// 发送modbus指令到设备
func (m *ModbusClient) SendRequestToDevice(subDeviceKey string, key string, value interface{}) error {
	subDevice, ok := m.SubDeviceReqMap[subDeviceKey]
	if !ok {
		return fmt.Errorf("sub device not found")
	}
	// 功能码
	functionCode := subDevice.DeviceConfig.FunctionCode
	// 起始地址
	startingAddress := subDevice.DeviceConfig.StartingAddress
	// 数据类型
	dataType := subDevice.DeviceConfig.DataType
	// 根据数据类型计算出地址步长
	var addressStep int
	switch dataType {
	case "int":
		addressStep = 1
	case "int16-2":
		addressStep = 1
	case "uint16-2":
		addressStep = 1
	case "int32-4":
		addressStep = 2
	case "uint32-4":
		addressStep = 2
	case "float32-4":
		addressStep = 2
	case "int64-8":
		addressStep = 4
	case "uint64-8":
		addressStep = 4
	case "float64-8":
		addressStep = 4
	}
	// 字节序
	byteOrder := subDevice.DeviceConfig.ByteOrder
	// key是属性名，value是属性值，遍历subDevice.key，计算出key的起始地址

	keyList := strings.Split(subDevice.DeviceConfig.Key, ",")
	zaplog.Logger.Info("keyList", zap.String("keyList", fmt.Sprintf("%v", keyList)))
	zaplog.Logger.Info("key", zap.String("key", key))
	for i, k := range keyList {
		if k == key {
			startingAddress = startingAddress + uint16(i*addressStep)
			// 组装报文
			message, err := m.AssembleMessage(functionCode, startingAddress, dataType, value, byteOrder, subDevice.DeviceConfig.DeviceAddress)
			if err != nil {
				zaplog.Logger.Error("assemble message failed", zap.Error(err))
				continue
			}
			// 发送报文
			zaplog.Logger.Info("send message to server", zap.String("message", string(message)))
			_, err = m.Conn.Write(message)
			if err != nil {
				zaplog.Logger.Error("write to server failed", zap.Error(err))
				continue
			}
		}
	}
	return nil
}

func (m *ModbusClient) AssembleMessage(functionCode uint8, startingAddress uint16, dataType string, value interface{}, byteOrder string, deviceAddress uint8) ([]byte, error) {
	var command []byte
	switch functionCode {
	case 1:
		functionCode = 5
	case 3:
		functionCode = 6
	}

	switch m.Mode {
	case "RTU":
		command = append(command, deviceAddress)
		command = append(command, functionCode)
		registerAddressBytes := make([]byte, 2)
		binary.BigEndian.PutUint16(registerAddressBytes, startingAddress)
		command = append(command, registerAddressBytes...)
		if functionCode == uint8(5) {
			// 判断value是不是数字
			var valueInt int
			switch v := value.(type) {
			case float64:
				valueInt = int(v)
			}
			// 定义线圈状态
			var coilStatus uint16
			if valueInt == 0 {
				coilStatus = 0
			}
			if valueInt == 1 {
				coilStatus = 0xff00
			}
			// 定义线圈状态字节
			var coilStatusBytes []byte
			coilStatusBytes = append(coilStatusBytes, byte(coilStatus>>8))
			coilStatusBytes = append(coilStatusBytes, byte(coilStatus))
			command = append(command, coilStatusBytes...)
		} else if functionCode == uint8(6) {
			valueBytes, err := convertToBytes(dataType, value, byteOrder)
			if err != nil {
				return nil, err
			}
			command = append(command, valueBytes...)
		}
		crc := calculateCRC(command)
		crcBytes := make([]byte, 2)
		binary.LittleEndian.PutUint16(crcBytes, crc)
		command = append(command, crcBytes...)
	case "TCP":
		header := make([]byte, 6)
		binary.BigEndian.PutUint16(header, 0)             // Transaction Identifier
		binary.BigEndian.PutUint16(header[2:], 0)         // Protocol Identifier
		binary.BigEndian.PutUint16(header[4:], uint16(6)) // Length
		command = append(command, header...)
		command = append(command, deviceAddress)
		command = append(command, functionCode)
		registerAddressBytes := make([]byte, 2)
		binary.BigEndian.PutUint16(registerAddressBytes, startingAddress)
		command = append(command, registerAddressBytes...)
		if functionCode == uint8(5) {
			// 判断value是不是数字
			valueInt, ok := value.(int)
			if !ok {
				return nil, fmt.Errorf("value is not int")
			}
			// 定义线圈状态
			var coilStatus uint16
			if valueInt == 0 {
				coilStatus = 0
			}
			if valueInt == 1 {
				coilStatus = 0xff00
			}
			// 定义线圈状态字节
			var coilStatusBytes []byte
			coilStatusBytes = append(coilStatusBytes, byte(coilStatus>>8))
			coilStatusBytes = append(coilStatusBytes, byte(coilStatus))
			command = append(command, coilStatusBytes...)
		} else if functionCode == uint8(6) {
			valueBytes, err := convertToBytes(dataType, value, byteOrder)
			if err != nil {
				return nil, err
			}
			command = append(command, valueBytes...)
		}
	}

	return command, nil
}

func convertToBytes(dataType string, dataValue interface{}, byteOrder string) ([]byte, error) {
	var buffer bytes.Buffer

	// 设置字节序
	var order binary.ByteOrder
	if byteOrder == "big" {
		order = binary.BigEndian
	} else if byteOrder == "little" {
		order = binary.LittleEndian
	} else {
		return nil, fmt.Errorf("unsupported byte order: %s", byteOrder)
	}

	// 根据数据类型将数据值写入buffer
	switch dataType {
	case "uint16-2":
		if value, ok := dataValue.(uint16); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not uint16")
		}
	case "uint32-4":
		if value, ok := dataValue.(uint32); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not uint32")
		}
	case "uint64-8":
		if value, ok := dataValue.(uint64); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not uint64")
		}
	case "int16-2":
		if value, ok := dataValue.(int16); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not int16")
		}
	case "int32-4":
		if value, ok := dataValue.(int32); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not int32")
		}
	case "int64-8":
		if value, ok := dataValue.(int64); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not int64")
		}
	case "float32-4":
		if value, ok := dataValue.(float32); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not float32")
		}
	case "float64-8":
		if value, ok := dataValue.(float64); ok {
			err := binary.Write(&buffer, order, value)
			if err != nil {
				return nil, err
			}
		} else {
			return nil, fmt.Errorf("dataValue is not float64")
		}
	default:
		return nil, fmt.Errorf("unsupported data type: %s", dataType)
	}

	return buffer.Bytes(), nil
}

// 以小端返回
func calculateCRC(data []byte) uint16 {
	crc := uint16(0xFFFF)
	for _, v := range data {
		crc ^= uint16(v)
		for i := 0; i < 8; i++ {
			if crc&1 == 1 {
				crc = (crc >> 1) ^ 0xA001
			} else {
				crc = crc >> 1
			}
		}
	}

	// 交换高低字节来得到小端格式的值
	return (crc<<8)&0xFF00 | (crc>>8)&0x00FF
}

func CalculateCRC_A(data []byte) uint16 {
	const polynomial = 0xA001
	crc := uint16(0xFFFF)

	for _, b := range data {
		crc ^= uint16(b)
		for i := 0; i < 8; i++ {
			bit := crc & 1
			crc >>= 1
			if bit == 1 {
				crc ^= polynomial
			}
		}
	}
	return crc
}
func Crc16(data []byte) uint16 {
	const polynomial = 0xA001
	var crc = uint16(0xFFFF)

	for _, byteVal := range data {
		crc ^= uint16(byteVal)
		for i := 0; i < 8; i++ {
			if (crc & 0x0001) != 0 {
				crc = (crc >> 1) ^ polynomial
			} else {
				crc >>= 1
			}
		}
	}
	return crc
}
